#! /bin/sh /usr/share/dpatch/dpatch-run
## 03_ipv6.dpatch by Petr Písař <petrp@users.sf.net>
##
## All lines beginning with `## DP:' are a description of the patch.
## DP: IPv6 support for privoxy.
## DP: http://xpisar.wz.cz/privoxy-ipv6/privoxy-3.0.10-ipv6-all-6.diff
## DP: Former versions by Lionel Elie Mamane <lionel@mamane.lu>

@DPATCH@
diff -urNad privoxy~/configure.in privoxy/configure.in
--- privoxy~/configure.in
+++ privoxy/configure.in
@@ -1237,7 +1237,7 @@
 AC_TYPE_SIGNAL
 dnl uncommenting does not work for swa. suse linux
 dnl AC_FUNC_STAT
-AC_CHECK_FUNCS([access atexit getcwd gethostbyaddr gethostbyaddr_r gethostbyname gethostbyname_r gettimeofday inet_ntoa localtime_r memchr memmove memset poll putenv random regcomp select setlocale snprintf socket strchr strdup strerror strftime strlcat strlcpy strptime strstr strtoul timegm tzset])
+AC_CHECK_FUNCS([access atexit getaddrinfo getcwd gethostbyaddr gethostbyaddr_r gethostbyname gethostbyname_r getnameinfo gettimeofday inet_ntoa localtime_r memchr memmove memset poll putenv random regcomp select setlocale snprintf socket strchr strdup strerror strftime strlcat strlcpy strptime strstr strtoul timegm tzset])
 
 
 dnl =================================================================
diff -urNad privoxy~/doc/source/p-config.sgml privoxy/doc/source/p-config.sgml
--- privoxy~/doc/source/p-config.sgml
+++ privoxy/doc/source/p-config.sgml
@@ -1213,9 +1213,9 @@
   <term>Effect if unset:</term>
   <listitem>
    <para>
-    Bind to 127.0.0.1 (localhost), port 8118. This is suitable and recommended for
-    home users who run <application>Privoxy</application> on the same machine as
-    their browser.
+    Bind to 127.0.0.1 (IPv4 localhost), port 8118. This is suitable and
+    recommended for home users who run <application>Privoxy</application> on
+    the same machine as their browser.
    </para>
   </listitem>
  </varlistentry>
@@ -1231,6 +1231,9 @@
     will need to override the default.
    </para>
    <para>
+    IPv6 address containing colons has to be quoted by brackets.
+   </para>
+   <para>
     If you leave out the IP address, <application>Privoxy</application> will
     bind to all interfaces (addresses) on your machine and may become reachable
     from the Internet. In that case, consider using <link
@@ -1259,6 +1262,15 @@
   listen-address  192.168.0.1:8118
 </programlisting>
    </para>
+   <para>
+    Suppose you are running <application>Privoxy</application> on IPv6 capable
+    machine and you want to listen on IPv6 loopback device:
+   </para>
+   <para>
+    <programlisting>
+  listen-address [::1]:8118
+</programlisting>
+   </para>
   </listitem>
  </varlistentry>
 </variablelist>
@@ -1611,23 +1623,41 @@
   <term>Type of value:</term>
   <listitem>
    <para>
-    <replaceable class="parameter">src_addr</replaceable>[/<replaceable class="parameter">src_masklen</replaceable>]
-    [<replaceable class="parameter">dst_addr</replaceable>[/<replaceable class="parameter">dst_masklen</replaceable>]]
+    <replaceable class="parameter">src_addr</replaceable>[:<replaceable class="parameter">port</replaceable>][/<replaceable class="parameter">src_masklen</replaceable>]
+    [<replaceable class="parameter">dst_addr</replaceable>[:<replaceable class="parameter">port</replaceable>][/<replaceable class="parameter">dst_masklen</replaceable>]]
    </para>
    <para>
     Where <replaceable class="parameter">src_addr</replaceable> and 
-   <replaceable class="parameter">dst_addr</replaceable> are IP addresses in dotted decimal notation or valid
-    DNS names, and <replaceable class="parameter">src_masklen</replaceable> and
+   <replaceable class="parameter">dst_addr</replaceable> are IPv4 addresses in dotted decimal notation or valid
+    DNS names, <replaceable class="parameter">port</replaceable> is port
+    number, and <replaceable class="parameter">src_masklen</replaceable> and
     <replaceable class="parameter">dst_masklen</replaceable> are subnet masks in CIDR notation, i.e. integer
     values from 2 to 30 representing the length (in bits) of the network address. The masks and the whole
     destination part are optional.
    </para>
+   <para>
+    If your system implements
+    <ulink url="http://tools.ietf.org/html/rfc2553">RFC 2553</ulink>, then
+    <replaceable class="parameter">src_addr</replaceable> and <replaceable
+    class="parameter">dst_addr</replaceable> can be IPv6 addresses delimeted by
+    brackets, <replaceable class="parameter">port</replaceable> can be number
+    or service name, and
+    <replaceable class="parameter">src_masklen</replaceable> and
+    <replaceable class="parameter">dst_masklen</replaceable> can be number
+    from 0 to 128.
+   </para>
   </listitem>
  </varlistentry>
  <varlistentry>
   <term>Default value:</term>
   <listitem>
    <para><emphasis>Unset</emphasis></para>
+   <para>
+    No <replaceable class="parameter">port</replaceable> means match any port
+    and no <replaceable class="parameter">src_masklen</replaceable> or
+    no <replaceable class="parameter">src_masklen</replaceable> means exactly
+    given IP address (i.e. 32 for IPv4 and 128 for IPv6).
+   </para>
   </listitem>
  </varlistentry>
  <varlistentry>
@@ -1677,6 +1707,13 @@
     IP addresses, only the first one is used.
    </para>
    <para>
+     Some systems allows IPv4 client to connect to IPv6 server socket.
+     Then the client's IPv4 address will be translated by system into
+     IPv6 address space with special prefix ::ffff/96 (so called IPv4
+     mapped IPv6 address). <application>Privoxy</application> can handle it
+     and maps such ACL addresses automatically.
+   </para>
+   <para>
     Denying access to particular sites by ACL may have undesired side effects
     if the site in question is hosted on a machine which also hosts other sites
     (most sites are).
@@ -1717,6 +1754,24 @@
   deny-access    192.168.45.73    www.dirty-stuff.example.com
 </screen>
    </para>
+   <para>
+     Allow access from IPv4 network 192.0.2.0/24 even if listening on
+     IPv6 wild card address (where supported by operating system):
+   </para>
+   <para>
+    <programlisting>
+  permit-access  192.0.2.0/24
+</programlisting>
+   </para>
+   <para>
+     This is equivalent to the following line even if listening on IPv4
+     address (where supported by operating system):
+   </para>
+   <para>
+    <programlisting>
+  permit-access  [::ffff:192.0.2.0]/120
+</programlisting>
+   </para>
   </listitem>
  </varlistentry>
 </variablelist>
@@ -1838,7 +1893,7 @@
     denote <quote>all URLs</quote>.
     <replaceable class="parameter">http_parent</replaceable>[:<replaceable class="parameter">port</replaceable>]
     is the DNS name or IP address of the parent HTTP proxy through which the requests should be forwarded,
-    optionally followed by its listening port (default: 8080).
+    optionally followed by its listening port (default: 8000).
     Use a single dot (<literal>.</literal>) to denote <quote>no forwarding</quote>.
    </para>
   </listitem>
@@ -1865,6 +1920,16 @@
     forwarded to another HTTP proxy but are made directly to the web servers.
    </para>
    <para>
+    <replaceable class="parameter">http_parent</replaceable> can be IPv6
+    numerical address (if
+    <ulink url="http://tools.ietf.org/html/rfc2553">RFC 2553</ulink> is
+    implemented). However not to clash with port delimiter, quote
+    whole IP address with brackets. On the other hand <replaceable
+    class="parameter">target_pattern</replaceable> containing IPv6 address
+    must be delimited by angle brackets (normal brackets are reserved for
+    regular expression already).
+   </para>
+   <para>
     Multiple lines are OK, they are checked in sequence, and the last match wins.
    </para>
   </listitem>
@@ -1891,6 +1956,24 @@
   forward   .isp.example.net   .
 </screen>
    </para>
+   <para>
+    Parent proxy specified by IPv6 address:
+   </para>
+   <para>
+    <programlisting>
+  foward   /                   [2001:DB8::1]:8000
+</programlisting>
+   </para>
+   <para>
+    Suppose your parent proxy doesn't support IPv6:
+   </para>
+   <para>
+    <programlisting>
+  forward  /                        parent-proxy.example.org:8000 
+  forward  ipv6-server.example.org  .
+  forward  &lt;[2-3][0-9a-f][0-9a-f][0-9a-f]:*&gt;   .
+</programlisting>
+   </para>
   </listitem>
  </varlistentry>
 </variablelist>
@@ -1963,6 +2046,18 @@
     With <literal>forward-socks5</literal> the DNS resolution will happen on the remote server as well.
    </para>
    <para>
+    <replaceable class="parameter">socks_proxy</replaceable> and
+    <replaceable class="parameter">http_parent</replaceable> can be IPv6
+    numerical address (if
+    <ulink url="http://tools.ietf.org/html/rfc2553">RFC 2553</ulink> is
+    implemented). However not to clash with port
+    delimiter, quote whole IP address with brackets. On the other
+    hand <replaceable class="parameter">target_pattern</replaceable> containing
+    IPv6 address must be delimited by angle brackets (normal brackets are
+    reserved for regular expression already). The only exception is SOCKS 4
+    version where only IPv4 is suppored. 
+   </para>
+   <para>
     If <replaceable class="parameter">http_parent</replaceable> is <quote>.</quote>, then requests are not
     forwarded to another HTTP proxy but are made (HTTP-wise) directly to the web servers, albeit through
     a SOCKS proxy.
diff -urNad privoxy~/doc/source/user-manual.sgml privoxy/doc/source/user-manual.sgml
--- privoxy~/doc/source/user-manual.sgml
+++ privoxy/doc/source/user-manual.sgml
@@ -2147,12 +2147,12 @@
  
 <para>
  Generally, an URL pattern has the form
- <literal>&lt;domain&gt;/&lt;path&gt;</literal>, where both the
- <literal>&lt;domain&gt;</literal> and <literal>&lt;path&gt;</literal> are
- optional. (This is why the special <literal>/</literal> pattern matches all
- URLs). Note that the protocol portion of the URL pattern (e.g.
- <literal>http://</literal>) should <emphasis>not</emphasis> be included in
- the pattern. This is assumed already!
+ <literal>&lt;domain&gt;&lt;port&gt;/&lt;path&gt;</literal>, where both the
+ <literal>&lt;domain&gt;</literal> and <literal>&lt;port&gt;</literal>
+ and <literal>&lt;path&gt;</literal> are optional. (This is why the special
+ <literal>/</literal> pattern matches all URLs). Note that the protocol
+ portion of the URL pattern (e.g. <literal>http://</literal>) should
+ <emphasis>not</emphasis> be included in the pattern. This is assumed already!
 </para>
 <para>
  The pattern matching syntax is different for the domain and path parts of
@@ -2161,6 +2161,12 @@
  <ulink url="http://en.wikipedia.org/wiki/Regular_expressions"><quote>Regular
   Expressions</quote></ulink> (POSIX 1003.2).
 </para>
+<para>
+ The port part of pattern is decimal port number preceeded by a colon
+ (<literal>:</literal>). If domain part contains numeric IPv6 address, you
+ will need to quote the domain part by angle brackets
+ (<literal>&lt;</literal>, <literal>&gt;</literal>).
+</para>
 
 <variablelist>
  <varlistentry>
@@ -2211,6 +2217,23 @@
   </listitem>
  </varlistentry>
  <varlistentry>
+  <term><literal>:8000/</literal></term>
+  <listitem>
+   <para>
+    Matches any URL pointing to TCP port 8000.
+   </para>
+  </listitem>
+ </varlistentry>
+ <varlistentry>
+  <term><literal>&lt;2001:db8::1&gt;/</literal></term>
+  <listitem>
+   <para>
+    Matches any URL having <literal>2001:db8::1</literal> as a domain.
+    (Note that real URL uses plain brackets, not an angle brackets.)
+   </para>
+  </listitem>
+ </varlistentry>
+ <varlistentry>
   <term><literal>index.html</literal></term>
   <listitem>
    <para>
diff -urNad privoxy~/filters.c privoxy/filters.c
--- privoxy~/filters.c
+++ privoxy/filters.c
@@ -665,6 +665,11 @@
 #include <string.h>
 #include <assert.h>
 
+#ifdef HAVE_GETADDRINFO
+#include <netdb.h>
+#include <sys/socket.h>
+#endif /* def HAVE_GETADDRINFO */
+
 #ifndef _WIN32
 #ifndef __OS2__
 #include <unistd.h>
@@ -712,6 +717,152 @@
 static jb_err prepare_for_filtering(struct client_state *csp);
 
 #ifdef FEATURE_ACL
+#ifdef HAVE_GETADDRINFO
+/*********************************************************************
+ *
+ * Function    :  sockaddr_storage_to_ip
+ *
+ * Description :  Access internal structure of sockaddr_storage
+ *
+ * Parameters  :
+ *          1  :  addr = socket address
+ *          2  :  ip   = IP address as array of octets in network order
+ *                       (it points into addr)
+ *          3  :  len  = length of IP address in octets
+ *          4  :  port = port number in network order;
+ *
+ * Returns     :  0 = no errror; otherwise 
+ *
+ *********************************************************************/
+int sockaddr_storage_to_ip(const struct sockaddr_storage *addr, uint8_t **ip,
+      unsigned int *len, in_port_t **port)
+{
+   if (!addr)
+   {
+      return(-1);
+   }
+
+   switch (addr->ss_family)
+   {
+      case AF_INET:
+         if (len)
+         {
+            *len = 4;
+         }
+         if (ip)
+         {
+            *ip = (uint8_t *)
+               &(( (struct sockaddr_in *) addr)->sin_addr.s_addr);
+         }
+         if (port)
+         {
+            *port = &((struct sockaddr_in *) addr)->sin_port;
+         }
+         break;
+
+      case AF_INET6:
+         if (len)
+         {
+            *len = 16;
+         }
+         if (ip)
+         {
+            *ip = (uint8_t *)
+               &(( (struct sockaddr_in6 *) addr)->sin6_addr.s6_addr);
+         }
+         if (port)
+         {
+            *port = &((struct sockaddr_in6 *) addr)->sin6_port;
+         }
+         break;
+
+      default:
+         /* Unsupported address family */
+         return(-1);
+   }
+
+   return(0);
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  match_sockaddr
+ *
+ * Description :  Check whether address matches network (IP address and port)
+ *
+ * Parameters  :
+ *          1  :  network = socket address of subnework
+ *          3  :  netmask = network mask as socket address 
+ *          2  :  address = checked socket address against given network
+ *
+ * Returns     :  0 = doesn't match; 1 = does match
+ *
+ *********************************************************************/
+int match_sockaddr(const struct sockaddr_storage *network,
+      const struct sockaddr_storage *netmask,
+      const struct sockaddr_storage *address)
+{
+   uint8_t *network_addr, *netmask_addr, *address_addr;
+   unsigned int addr_len;
+   in_port_t *network_port, *netmask_port, *address_port;
+   int i;
+
+   if (network->ss_family != netmask->ss_family) 
+   {
+      /* This should never happen */
+      log_error(LOG_LEVEL_ERROR,
+            "Internal error at %s:%llu: network and netmask differ in family",
+            __FILE__, __LINE__);
+      return 0;
+   }
+
+   sockaddr_storage_to_ip(network, &network_addr, &addr_len, &network_port);
+   sockaddr_storage_to_ip(netmask, &netmask_addr, NULL, &netmask_port);
+   sockaddr_storage_to_ip(address, &address_addr, NULL, &address_port);
+
+   /* Check for family */
+   if (network->ss_family == AF_INET && address->ss_family == AF_INET6 &&
+         IN6_IS_ADDR_V4MAPPED(address_addr))
+   {
+      /* Map AF_INET6 V4MAPPED address into AF_INET */
+      address_addr += 12;
+      addr_len = 4;
+   }
+   else if (network->ss_family == AF_INET6 && address->ss_family == AF_INET &&
+         IN6_IS_ADDR_V4MAPPED(network_addr))
+   {
+      /* Map AF_INET6 V4MAPPED network into AF_INET */
+      network_addr += 12;
+      netmask_addr += 12;
+      addr_len = 4;
+   }
+   else if (network->ss_family != address->ss_family)
+   {
+      return 0;
+   }
+
+   /* XXX: Port check is signaled in netmask */
+   if (*netmask_port && *network_port != *address_port)
+   {
+      return 0;
+   }
+
+   /* TODO: Optimize by checking by words insted of octets */
+   for (i=0; i < addr_len && netmask_addr[i]; i++)
+   {
+      if ( (network_addr[i] & netmask_addr[i]) !=
+           (address_addr[i] & netmask_addr[i]) )
+      {
+         return 0;
+      }
+   }
+   
+   return 1;
+}
+#endif /* def HAVE_GETADDRINFO */
+
+
 /*********************************************************************
  *
  * Function    :  block_acl
@@ -741,7 +892,13 @@
    /* search the list */
    while (acl != NULL)
    {
-      if ((csp->ip_addr_long & acl->src->mask) == acl->src->addr)
+      if (
+#ifdef HAVE_GETADDRINFO
+            match_sockaddr(&acl->src->addr, &acl->src->mask, &csp->tcp_addr) 
+#else
+            (csp->ip_addr_long & acl->src->mask) == acl->src->addr
+#endif
+            )
       {
          if (dst == NULL)
          {
@@ -751,8 +908,23 @@
                return(0);
             }
          }
-         else if ( ((dst->addr & acl->dst->mask) == acl->dst->addr)
-           && ((dst->port == acl->dst->port) || (acl->dst->port == 0)))
+         else if (
+#ifdef HAVE_GETADDRINFO
+               /* XXX: Undefined acl->dst is full of zeros and should be
+                * considered as wildcard address.
+                * sockaddr_storage_to_ip() failes on such dst because of
+                * uknown sa_familly on glibc. However this test is not
+                * portable.
+                *
+                * So, we signal the acl->dst is wildcard in wildcard_dst.
+                */
+               acl->wildcard_dst ||
+                  match_sockaddr(&acl->dst->addr, &acl->dst->mask, &dst->addr)
+#else
+               ((dst->addr & acl->dst->mask) == acl->dst->addr)
+           && ((dst->port == acl->dst->port) || (acl->dst->port == 0))
+#endif
+           )
          {
             if (acl->action == ACL_PERMIT)
             {
@@ -788,12 +960,24 @@
 int acl_addr(const char *aspec, struct access_control_addr *aca)
 {
    int i, masklength;
+#ifdef HAVE_GETADDRINFO
+   struct addrinfo hints, *result;
+   uint8_t *mask_data;
+   in_port_t *mask_port;
+   unsigned int addr_len;
+#else
    long port;
+#endif /* def HAVE_GETADDRINFO */
    char *p;
    char *acl_spec = NULL;
 
+#ifdef HAVE_GETADDRINFO
+   /* FIXME: Depend on ai_family */
+   masklength = 128;
+#else
    masklength = 32;
    port       =  0;
+#endif
 
    /*
     * Use a temporary acl spec copy so we can log
@@ -817,13 +1001,53 @@
       masklength = atoi(p);
    }
 
-   if ((masklength < 0) || (masklength > 32))
+   if ((masklength < 0) ||
+#ifdef HAVE_GETADDRINFO
+         (masklength > 128)
+#else
+         (masklength > 32)
+#endif
+         )
    {
       freez(acl_spec);
       return(-1);
    }
 
-   if ((p = strchr(acl_spec, ':')) != NULL)
+   if (*acl_spec == '[' && NULL != (p = strchr(acl_spec, ']')))
+   {
+      *p = '\0';
+      memmove(acl_spec, acl_spec + 1, (size_t) (p - acl_spec));
+
+      if (*++p != ':')
+      {
+         p = NULL;
+      }
+   }
+   else
+   {
+      p = strchr(acl_spec, ':');
+   }
+
+#ifdef HAVE_GETADDRINFO
+   memset(&hints, 0, sizeof(struct addrinfo));
+   hints.ai_family = AF_UNSPEC;
+   hints.ai_socktype = SOCK_STREAM;
+
+   i = getaddrinfo(acl_spec, (p) ? ++p : NULL, &hints, &result);
+   freez(acl_spec);
+
+   if (i != 0)
+   {
+      log_error(LOG_LEVEL_ERROR, "Can not resolve [%s]:%s: %s", acl_spec, p,
+            gai_strerror(i));
+      return(-1);
+   }
+
+   /* TODO: Allow multihomed hostnames */
+   memcpy(&(aca->addr), result->ai_addr, sizeof(aca->addr));
+   freeaddrinfo(result);
+#else
+   if (p != NULL)
    {
       char *endptr;
 
@@ -847,8 +1071,49 @@
       /* XXX: This will be logged as parse error. */
       return(-1);
    }
+#endif /* def HAVE_GETADDRINFO */
 
    /* build the netmask */
+#ifdef HAVE_GETADDRINFO
+   /* Clip masklength according current family */
+   if (aca->addr.ss_family == AF_INET && masklength > 32)
+   {
+      masklength = 32;
+   }
+
+   aca->mask.ss_family = aca->addr.ss_family;
+   if (sockaddr_storage_to_ip(&aca->mask, &mask_data, &addr_len, &mask_port))
+   {
+      return(-1);
+   }
+
+   if (p)
+   {
+      /* Port number in ACL has been specified, check ports in future */
+      *mask_port = 1;
+   }
+
+   /* XXX: This could be optimized to operate on whole words instead of octets
+    * (128-bit CPU could do it in one iteration). */
+   /* Octets after prefix can be ommitted because of previous initialization
+    * to zeros. */
+   for (i=0; i < addr_len && masklength; i++)
+   {
+      if (masklength >= 8)
+      {
+         mask_data[i] = 0xFF;
+         masklength -= 8;
+      }
+      else
+      {
+         /* XXX: This assumes MSB of octet is on the left site. This should be
+          * true for all architectures or solved on link layer of OSI model. */
+         mask_data[i] = ~((1 << (8 - masklength)) - 1);
+         masklength = 0;
+      }
+   }
+
+#else
    aca->mask = 0;
    for (i=1; i <= masklength ; i++)
    {
@@ -859,6 +1124,7 @@
     * (i.e. save on the network portion of the address).
     */
    aca->addr = aca->addr & aca->mask;
+#endif /* def HAVE_GETADDRINFO */
 
    return(0);
 
@@ -2706,4 +2972,6 @@
   Local Variables:
   tab-width: 3
   end:
+
+  vim:softtabstop=3 shiftwidth=3
 */
diff -urNad privoxy~/jbsockets.c privoxy/jbsockets.c
--- privoxy~/jbsockets.c
+++ privoxy/jbsockets.c
@@ -357,19 +357,52 @@
  *********************************************************************/
 jb_socket connect_to(const char *host, int portnum, struct client_state *csp)
 {
+#ifdef HAVE_GETADDRINFO
+   struct addrinfo hints, *result, *rp;
+   char service[6];
+   int retval;
+#else
    struct sockaddr_in inaddr;
-   jb_socket fd;
    unsigned int addr;
+#endif /* def HAVE_GETADDRINFO */
+   jb_socket fd;
    fd_set wfds;
    struct timeval tv[1];
 #if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA)
    int   flags;
 #endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) */
+   int connect_failed;
 
 #ifdef FEATURE_ACL
    struct access_control_addr dst[1];
 #endif /* def FEATURE_ACL */
 
+#ifdef HAVE_GETADDRINFO
+   retval = snprintf(service, sizeof(service), "%d", portnum);
+   if (-1 == retval || sizeof(service) <= retval)
+   {
+      log_error(LOG_LEVEL_ERROR,
+            "Port number (%d) ASCII decimal representation doesn't fit into 6 bytes",
+            portnum);
+      csp->http->host_ip_addr_str = strdup("unknown");
+      return(JB_INVALID_SOCKET);
+   }
+
+   memset((char *)&hints, 0, sizeof hints);
+   hints.ai_family = AF_UNSPEC;
+   hints.ai_socktype = SOCK_STREAM;
+   hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; /* avoid service look-up */
+   if ((retval = getaddrinfo(host, service, &hints, &result)))
+   {
+      log_error(LOG_LEVEL_INFO,
+            "Can not resolve %s: %s", host, gai_strerror(retval));
+      csp->http->host_ip_addr_str = strdup("unknown");
+      return(JB_INVALID_SOCKET);
+   }
+
+   for (rp = result; rp != NULL; rp = rp->ai_next)
+   {
+#else
    memset((char *)&inaddr, 0, sizeof inaddr);
 
    if ((addr = resolve_hostname_to_ip(host)) == INADDR_NONE)
@@ -377,10 +410,15 @@
       csp->http->host_ip_addr_str = strdup("unknown");
       return(JB_INVALID_SOCKET);
    }
+#endif /* def HAVE_GETADDRINFO */
 
 #ifdef FEATURE_ACL
+#ifdef HAVE_GETADDRINFO
+   memcpy(&dst->addr, rp->ai_addr, sizeof(dst->addr));
+#else
    dst->addr = ntohl(addr);
    dst->port = portnum;
+#endif /* def HAVE_GETADDRINFO */
 
    if (block_acl(dst, csp))
    {
@@ -389,14 +427,43 @@
 #else
       errno = EPERM;
 #endif
+#ifdef HAVE_GETADDRINFO
+      continue;
+#else
       return(JB_INVALID_SOCKET);
+#endif /* def HAVE_GETADDRINFO */
    }
 #endif /* def FEATURE_ACL */
 
+#ifdef HAVE_GETNAMEINFO
+   csp->http->host_ip_addr_str = malloc(NI_MAXHOST);
+   retval = getnameinfo(rp->ai_addr, rp->ai_addrlen,
+         csp->http->host_ip_addr_str, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
+   if (!csp->http->host_ip_addr_str || retval)
+   {
+      log_error(LOG_LEVEL_ERROR, "Can not save csp->http->host_ip_addr_str: %s",
+            (csp->http->host_ip_addr_str) ? gai_strerror(retval) :
+            "Insufficient memory");
+      freez(csp->http->host_ip_addr_str);
+      continue;
+   }
+#else
    inaddr.sin_addr.s_addr = addr;
    inaddr.sin_family      = AF_INET;
    csp->http->host_ip_addr_str = strdup(inet_ntoa(inaddr.sin_addr));
+#endif /* def HAVE_GETNAMERINFO */
 
+#ifdef HAVE_GETADDRINFO
+#ifdef _WIN32
+   if ((fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) ==
+         JB_INVALID_SOCKET)
+#else
+   if ((fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) < 0)
+#endif
+   {
+      continue;
+   }
+#else
 #ifndef _WIN32
    if (sizeof(inaddr.sin_port) == sizeof(short))
 #endif /* ndef _WIN32 */
@@ -418,6 +485,7 @@
    {
       return(JB_INVALID_SOCKET);
    }
+#endif /* HAVE_GETADDRINFO */
 
 #ifdef TCP_NODELAY
    {  /* turn off TCP coalescence */
@@ -434,7 +502,12 @@
    }
 #endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */
 
+   connect_failed = 0;
+#ifdef HAVE_GETADDRINFO
+   while (connect(fd, rp->ai_addr, rp->ai_addrlen) == JB_INVALID_SOCKET)
+#else
    while (connect(fd, (struct sockaddr *) & inaddr, sizeof inaddr) == JB_INVALID_SOCKET)
+#endif /* HAVE_GETADDRINFO */
    {
 #ifdef _WIN32
       if (errno == WSAEINPROGRESS)
@@ -454,9 +527,18 @@
 #endif /* __OS2__ */
       {
          close_socket(fd);
-         return(JB_INVALID_SOCKET);
+         connect_failed = 1;
+         break;
       }
    }
+   if (connect_failed)
+   {
+#ifdef HAVE_GETADDRINFO
+         continue;
+#else
+         return(JB_INVALID_SOCKET);
+#endif
+   }
 
 #if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
    if (flags != -1)
@@ -477,8 +559,32 @@
    if (select((int)fd + 1, NULL, &wfds, NULL, tv) <= 0)
    {
       close_socket(fd);
+#ifdef HAVE_GETADDRINFO
+      continue;
+#else
       return(JB_INVALID_SOCKET);
+#endif
    }
+   
+#ifdef HAVE_GETADDRINFO
+   break; /* for */
+   }
+
+   freeaddrinfo(result);
+   if (!rp)
+   {
+      log_error(LOG_LEVEL_INFO, "Could not connect to TCP/[%s]:%s", host, service);
+      return(JB_INVALID_SOCKET);
+   }
+   /* XXX: Current connection verification (EINPROGRESS && select() for
+    * writing) is not sufficient. E.g. on my Linux-2.6.27 with glibc-2.6
+    * select returns socket ready for writing, however subsequential write(2)
+    * fails with ENOCONNECT. Read Linux connect(2) man page about non-blocking
+    * sockets.
+    * Thus we can not log here the socket is connected. */
+   /*log_error(LOG_LEVEL_INFO, "Connected to TCP/[%s]:%s", host, service);*/
+#endif
+
    return(fd);
 
 }
@@ -677,7 +783,16 @@
  *********************************************************************/
 int bind_port(const char *hostnam, int portnum, jb_socket *pfd)
 {
+#ifdef HAVE_GETADDRINFO
+   struct addrinfo hints;
+   struct addrinfo *result, *rp;
+   /* TODO: portnum shuld be string to allow symbolic service names in
+    * configuration and to avoid following int2string */
+   char servnam[6];
+   int retval;
+#else
    struct sockaddr_in inaddr;
+#endif /* def HAVE_GETADDRINFO */
    jb_socket fd;
 #ifndef _WIN32
    int one = 1;
@@ -685,6 +800,32 @@
 
    *pfd = JB_INVALID_SOCKET;
 
+#ifdef HAVE_GETADDRINFO
+   retval = snprintf(servnam, sizeof(servnam), "%d", portnum);
+   if (-1 == retval || sizeof(servnam) <= retval)
+   {
+      log_error(LOG_LEVEL_ERROR,
+            "Port number (%d) ASCII decimal representation doesn't fit into 6 bytes",
+            portnum);
+      return -1;
+   }
+
+   memset(&hints, 0, sizeof(struct addrinfo));
+   hints.ai_family=AF_UNSPEC;
+   hints.ai_socktype=SOCK_STREAM;
+   hints.ai_flags=AI_PASSIVE|AI_ADDRCONFIG;
+   hints.ai_protocol=0; /* Realy any stream protocol or TCP only */
+   hints.ai_canonname=NULL;
+   hints.ai_addr=NULL;
+   hints.ai_next=NULL;
+
+   if ((retval = getaddrinfo(hostnam, servnam, &hints, &result)))
+   {
+      log_error(LOG_LEVEL_ERROR,
+            "Can not resolve %s: %s", hostnam, gai_strerror(retval));
+      return -2;
+   }
+#else
    memset((char *)&inaddr, '\0', sizeof inaddr);
 
    inaddr.sin_family      = AF_INET;
@@ -707,8 +848,15 @@
       inaddr.sin_port = htonl((unsigned long) portnum);
    }
 #endif /* ndef _WIN32 */
+#endif /* def HAVE_GETADDRINFO */
 
+#ifdef HAVE_GETADDRINFO
+   for (rp = result; rp != NULL; rp = rp->ai_next)
+   {
+      fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+#else
    fd = socket(AF_INET, SOCK_STREAM, 0);
+#endif /* def HAVE_GETADDRINFO */
 
 #ifdef _WIN32
    if (fd == JB_INVALID_SOCKET)
@@ -716,7 +864,11 @@
    if (fd < 0)
 #endif
    {
+#ifdef HAVE_GETADDRINFO
+      continue;
+#else
       return(-1);
+#endif
    }
 
 #ifndef _WIN32
@@ -735,7 +887,11 @@
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one));
 #endif /* ndef _WIN32 */
 
+#ifdef HAVE_GETADDRINFO
+   if (bind(fd, rp->ai_addr, rp->ai_addrlen) < 0)
+#else
    if (bind(fd, (struct sockaddr *)&inaddr, sizeof(inaddr)) < 0)
+#endif
    {
 #ifdef _WIN32
       errno = WSAGetLastError();
@@ -744,15 +900,36 @@
       if (errno == EADDRINUSE)
 #endif
       {
+#ifdef HAVE_GETADDRINFO
+         freeaddrinfo(result);
+#endif
          close_socket(fd);
          return(-3);
       }
       else
       {
          close_socket(fd);
+#ifndef HAVE_GETADDRINFO
          return(-1);
       }
    }
+#else
+      }
+   }
+   else
+      /* bind() succeeded, escape from for-loop */
+      /* TODO: Support multiple listening sockets (e.g. localhost resolves to
+       * AF_INET and AF_INET6, but only fist address is used */
+      break;
+   }
+
+   freeaddrinfo(result);
+   if (rp == NULL)
+   {
+      /* All bind()s failed */
+      return(-1);
+   }
+#endif /* ndef HAVE_GETADDRINFO */
 
    while (listen(fd, MAX_LISTEN_BACKLOG) == -1)
    {
@@ -792,14 +969,20 @@
  *********************************************************************/
 void get_host_information(jb_socket afd, char **ip_address, char **hostname)
 {
+#ifdef HAVE_GETNAMEINFO
+   struct sockaddr_storage server;
+   int retval;
+#else
    struct sockaddr_in server;
    struct hostent *host = NULL;
+#endif /* HAVE_GETNAMEINFO */
 #if defined(_WIN32) || defined(__OS2__) || defined(__APPLE_CC__) || defined(AMIGA)
    /* according to accept_connection() this fixes a warning. */
-   int s_length;
+   int s_length, s_length_provided;
 #else
-   socklen_t s_length;
+   socklen_t s_length, s_length_provided;
 #endif
+#ifndef HAVE_GETNAMEINFO
 #if defined(HAVE_GETHOSTBYADDR_R_8_ARGS) ||  defined(HAVE_GETHOSTBYADDR_R_7_ARGS) || defined(HAVE_GETHOSTBYADDR_R_5_ARGS)
    struct hostent result;
 #if defined(HAVE_GETHOSTBYADDR_R_5_ARGS)
@@ -809,7 +992,8 @@
    int thd_err;
 #endif /* def HAVE_GETHOSTBYADDR_R_5_ARGS */
 #endif /* def HAVE_GETHOSTBYADDR_R_(8|7|5)_ARGS */
-   s_length = sizeof(server);
+#endif /* ifndef HAVE_GETNAMEINFO */
+   s_length = s_length_provided = sizeof(server);
 
    if (NULL != hostname)
    {
@@ -819,8 +1003,23 @@
 
    if (!getsockname(afd, (struct sockaddr *) &server, &s_length))
    {
+      if (s_length > s_length_provided)
+      {
+         log_error(LOG_LEVEL_ERROR, "getsockname() truncated server address");
+         return;
+      }
+#ifdef HAVE_GETNAMEINFO
+      *ip_address = malloc(NI_MAXHOST);
+      if ((retval = getnameinfo((struct sockaddr *) &server, s_length,
+                  *ip_address, NI_MAXHOST, NULL, 0, NI_NUMERICHOST))) {
+         log_error(LOG_LEVEL_ERROR, "Unable to print my own IP address: %s",
+               gai_strerror(retval));
+         freez(*ip_address);
+         return;
+      }
+#else
       *ip_address = strdup(inet_ntoa(server.sin_addr));
-
+#endif /* HAVE_GETNAMEINFO */
       if (NULL == hostname)
       {
          /*
@@ -829,6 +1028,16 @@
           */
          return;
       }
+
+#ifdef HAVE_GETNAMEINFO
+      *hostname = malloc(NI_MAXHOST);
+      if ((retval = getnameinfo((struct sockaddr *) &server, s_length,
+                  *hostname, NI_MAXHOST, NULL, 0, NI_NAMEREQD))) {
+         log_error(LOG_LEVEL_ERROR, "Unable to resolve my own IP address: %s",
+               gai_strerror(retval));
+         freez(*hostname);
+      }
+#else
 #if defined(HAVE_GETHOSTBYADDR_R_8_ARGS)
       gethostbyaddr_r((const char *)&server.sin_addr,
                       sizeof(server.sin_addr), AF_INET,
@@ -866,6 +1075,7 @@
       {
          *hostname = strdup(host->h_name);
       }
+#endif /* else def HAVE_GETNAMEINFO */
    }
 
    return;
@@ -890,7 +1100,13 @@
  *********************************************************************/
 int accept_connection(struct client_state * csp, jb_socket fd)
 {
+#ifdef HAVE_GETNAMEINFO
+   /* XXX: client is stored directly into csp->tcp_addr */
+#define client (csp->tcp_addr)
+   int retval;
+#else
    struct sockaddr_in client;
+#endif
    jb_socket afd;
 #if defined(_WIN32) || defined(__OS2__) || defined(__APPLE_CC__) || defined(AMIGA)
    /* Wierdness - fix a warning. */
@@ -919,8 +1135,21 @@
 #endif
 
    csp->cfd = afd;
+#ifdef HAVE_GETNAMEINFO
+   csp->ip_addr_str = malloc(NI_MAXHOST);
+   retval = getnameinfo((struct sockaddr *) &client, c_length,
+         csp->ip_addr_str, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
+   if (!csp->ip_addr_str || retval)
+   {
+      log_error(LOG_LEVEL_ERROR, "Can not save csp->ip_addr_str: %s",
+            (csp->ip_addr_str) ? gai_strerror(retval) : "Insuffcient memory");
+      freez(csp->ip_addr_str);
+   }
+#undef client
+#else
    csp->ip_addr_str  = strdup(inet_ntoa(client.sin_addr));
    csp->ip_addr_long = ntohl(client.sin_addr.s_addr);
+#endif /* def HAVE_GETNAMEINFO */
 
    return 1;
 
@@ -1051,4 +1280,6 @@
   Local Variables:
   tab-width: 3
   end:
+
+  vim:softtabstop=3 shiftwidth=3
 */
diff -urNad privoxy~/jcc.c privoxy/jcc.c
--- privoxy~/jcc.c
+++ privoxy/jcc.c
@@ -2710,7 +2710,7 @@
 
    if (fwd->forward_host)
    {
-      log_error(LOG_LEVEL_CONNECT, "via %s:%d to: %s",
+      log_error(LOG_LEVEL_CONNECT, "via [%s]:%d to: %s",
          fwd->forward_host, fwd->forward_port, http->hostport);
    }
    else
@@ -4483,4 +4483,6 @@
   Local Variables:
   tab-width: 3
   end:
+
+  vim:softtabstop=3 shiftwidth=3
 */
diff -urNad privoxy~/loadcfg.c privoxy/loadcfg.c
--- privoxy~/loadcfg.c
+++ privoxy/loadcfg.c
@@ -835,7 +835,6 @@
     * Set to defaults
     */
    config->multi_threaded            = 1;
-   config->hport                     = HADDR_PORT;
    config->buffer_limit              = 4096 * 1024;
    config->usermanual                = strdup(USER_MANUAL_URL);
    config->proxy_args                = strdup("");
@@ -1044,6 +1043,12 @@
                   break;
                }
             }
+#ifdef HAVE_GETADDRINFO
+            else
+            {
+               cur_acl->wildcard_dst = 1;
+            }
+#endif /* def HAVE_GETADDRINFO */
 
             /*
              * Add it to the list.  Note we reverse the list to get the
@@ -1193,7 +1198,18 @@
             {
                cur_fwd->forward_host = strdup(p);
 
-               if (NULL != (p = strchr(cur_fwd->forward_host, ':')))
+               if (*cur_fwd->forward_host == '[' && 
+                     NULL != (p = strchr(cur_fwd->forward_host, ']')))
+               {
+                  *p++ = '\0';
+                  memmove(cur_fwd->forward_host, cur_fwd->forward_host + 1,
+                        (size_t) (p - cur_fwd->forward_host));
+                  if (*p == ':')
+                  {
+                     cur_fwd->forward_port = atoi(++p);
+                  }
+               }
+               else if (NULL != (p = strchr(cur_fwd->forward_host, ':')))
                {
                   *p++ = '\0';
                   cur_fwd->forward_port = atoi(p);
@@ -1257,11 +1273,23 @@
             {
                cur_fwd->gateway_host = strdup(p);
 
-               if (NULL != (p = strchr(cur_fwd->gateway_host, ':')))
+               if (*cur_fwd->gateway_host == '[' && 
+                     NULL != (p = strchr(cur_fwd->gateway_host, ']')))
+               {
+                  *p++ = '\0';
+                  memmove(cur_fwd->gateway_host, cur_fwd->gateway_host + 1,
+                        (size_t) (p - cur_fwd->gateway_host));
+                  if (*p == ':')
+                  {
+                     cur_fwd->gateway_port = atoi(++p);
+                  }
+               }
+               else if (NULL != (p = strchr(cur_fwd->gateway_host, ':')))
                {
                   *p++ = '\0';
                   cur_fwd->gateway_port = atoi(p);
                }
+
                if (cur_fwd->gateway_port <= 0)
                {
                   cur_fwd->gateway_port = 1080;
@@ -1275,7 +1303,18 @@
             {
                cur_fwd->forward_host = strdup(p);
 
-               if (NULL != (p = strchr(cur_fwd->forward_host, ':')))
+               if (*cur_fwd->forward_host == '[' && 
+                     NULL != (p = strchr(cur_fwd->forward_host, ']')))
+               {
+                  *p++ = '\0';
+                  memmove(cur_fwd->forward_host, cur_fwd->forward_host + 1,
+                        (size_t) (p - cur_fwd->forward_host));
+                  if (*p == ':')
+                  {
+                     cur_fwd->forward_port = atoi(++p);
+                  }
+               }
+               else if (NULL != (p = strchr(cur_fwd->forward_host, ':')))
                {
                   *p++ = '\0';
                   cur_fwd->forward_port = atoi(p);
@@ -1345,11 +1384,23 @@
 
             cur_fwd->gateway_host = strdup(p);
 
-            if (NULL != (p = strchr(cur_fwd->gateway_host, ':')))
+            if (*cur_fwd->gateway_host == '[' && 
+                  NULL != (p = strchr(cur_fwd->gateway_host, ']')))
+            {
+               *p++ = '\0';
+               memmove(cur_fwd->gateway_host, cur_fwd->gateway_host + 1,
+                     (size_t) (p - cur_fwd->gateway_host));
+               if (*p == ':')
+               {
+                  cur_fwd->gateway_port = atoi(++p);
+               }
+            }
+            else if (NULL != (p = strchr(cur_fwd->gateway_host, ':')))
             {
                *p++ = '\0';
                cur_fwd->gateway_port = atoi(p);
             }
+
             if (cur_fwd->gateway_port <= 0)
             {
                cur_fwd->gateway_port = 1080;
@@ -1362,7 +1413,18 @@
             {
                cur_fwd->forward_host = strdup(p);
 
-               if (NULL != (p = strchr(cur_fwd->forward_host, ':')))
+               if (*cur_fwd->forward_host == '[' && 
+                     NULL != (p = strchr(cur_fwd->forward_host, ']')))
+               {
+                  *p++ = '\0';
+                  memmove(cur_fwd->forward_host, cur_fwd->forward_host + 1,
+                        (size_t) (p - cur_fwd->forward_host));
+                  if (*p == ':')
+                  {
+                     cur_fwd->forward_port = atoi(++p);
+                  }
+               }
+               else if (NULL != (p = strchr(cur_fwd->forward_host, ':')))
                {
                   *p++ = '\0';
                   cur_fwd->forward_port = atoi(p);
@@ -1512,6 +1574,12 @@
                   break;
                }
             }
+#ifdef HAVE_GETADDRINFO
+            else
+            {
+               cur_acl->wildcard_dst = 1;
+            }
+#endif /* def HAVE_GETADDRINFO */
 
             /*
              * Add it to the list.  Note we reverse the list to get the
@@ -1851,18 +1919,20 @@
 
    if ( NULL != config->haddr )
    {
-      if (NULL != (p = strchr(config->haddr, ':')))
+      if (*config->haddr == '[' && NULL != (p = strchr(config->haddr, ']')) &&
+            p[1] == ':' && 0 < (config->hport = atoi(p + 2)))
       {
-         *p++ = '\0';
-         if (*p)
-         {
-            config->hport = atoi(p);
-         }
+         *p='\0';
+         memmove((void *) config->haddr, config->haddr + 1,
+               (size_t) (p - config->haddr));
       }
-
-      if (config->hport <= 0)
+      else if (NULL != (p = strchr(config->haddr, ':')) &&
+            0 < (config->hport = atoi(p + 1)))
+      {
+         *p = '\0';
+      }
+      else 
       {
-         *--p = ':';
          log_error(LOG_LEVEL_FATAL, "invalid bind port spec %s", config->haddr);
          /* Never get here - LOG_LEVEL_FATAL causes program exit */
       }
@@ -2038,4 +2108,6 @@
   Local Variables:
   tab-width: 3
   end:
+
+  vim:softtabstop=3 shiftwidth=3
 */
diff -urNad privoxy~/project.h privoxy/project.h
--- privoxy~/project.h
+++ privoxy/project.h
@@ -710,6 +710,12 @@
 /* Needed for pcre choice */
 #include "config.h"
 
+#ifdef HAVE_GETADDRINFO
+/* Need for struct sockaddr_storage */
+#include <sys/socket.h>
+#endif
+
+
 /*
  * Include appropriate regular expression libraries.
  * Note that pcrs and pcre (native) are needed for cgi
@@ -861,16 +867,10 @@
 #define FOREVER 1
 
 /**
- * Default IP address to listen on, as a string.
- * Set to "127.0.0.1".
- */
-#define HADDR_DEFAULT   "127.0.0.1"
-
-/**
- * Default port to listen on, as a number.
- * Set to 8118.
+ * Default TCP/IP address to listen on, as a string.
+ * Set to "127.0.0.1:8118".
  */
-#define HADDR_PORT      8118
+#define HADDR_DEFAULT   "127.0.0.1:8118"
 
 
 /* Forward def for struct client_state */
@@ -1424,9 +1424,15 @@
    /** Client PC's IP address, as reported by the accept() function.
        As a string. */
    char *ip_addr_str;
+#ifdef HAVE_GETADDRINFO
+   /** Client PC's TCP address, as reported by the accept() function.
+       As a sockaddr. */
+   struct sockaddr_storage tcp_addr;
+#else
    /** Client PC's IP address, as reported by the accept() function.
        As a number. */
    unsigned long ip_addr_long;
+#endif /* def HAVE_GETADDRINFO */
 
    /** The URL that was requested */
    struct http_request http[1];
@@ -1660,9 +1666,14 @@
  */
 struct access_control_addr
 {
+#ifdef HAVE_GETADDRINFO
+   struct sockaddr_storage addr; /* <The TCP address in network order. */
+   struct sockaddr_storage mask; /* <The TCP mask in network order. */
+#else
    unsigned long addr;  /**< The IP address as an integer. */
    unsigned long mask;  /**< The network mask as an integer. */
    unsigned long port;  /**< The port number. */
+#endif /* HAVE_GETADDRINFO */
 };
 
 /**
@@ -1674,6 +1685,9 @@
 {
    struct access_control_addr src[1];  /**< Client IP address */
    struct access_control_addr dst[1];  /**< Website or parent proxy IP address */
+#ifdef HAVE_GETADDRINFO
+   int wildcard_dst;                   /** < dst address is wildcard */
+#endif
 
    short action;                       /**< ACL_PERMIT or ACL_DENY */
    struct access_control_list *next;   /**< The next entry in the ACL. */
@@ -1882,4 +1896,6 @@
   Local Variables:
   tab-width: 3
   end:
+
+  vim:softtabstop=3 shiftwidth=3
 */
diff -urNad privoxy~/urlmatch.c privoxy/urlmatch.c
--- privoxy~/urlmatch.c
+++ privoxy/urlmatch.c
@@ -540,8 +540,40 @@
          host = buf;
       }
 
+      /* Move after hostname before port number */
+      if (*host == '[')
+      {
+         /* Numeric IPv6 address delimited by brackets */
+         host++;
+         port = strchr(host, ']');
+
+         if (port == NULL)
+         {
+            /* Missing closing bracket */
+            freez(buf);
+            return JB_ERR_PARSE;
+         }
+
+         *port++='\0';
+         
+         if (*port == '\0')
+         {
+            port = NULL;
+         }
+         else if (*port != ':')
+         {
+            /* Garbage after closing bracket */
+            freez(buf);
+            return JB_ERR_PARSE;
+         }
+      }
+      else
+      {
+         /* Plain non-escaped hostname */ 
+         port = strchr(host, ':');
+      }
+
       /* check if url contains port */
-      port = strchr(host, ':');
       if (port != NULL)
       {
          /* Contains port */
@@ -844,7 +876,31 @@
       *p = '\0';
    }
 
-   p = strchr(buf, ':');
+   /* XXX: IPv6 numeric hostname contains colons, thus we need to delimit the
+    * hostname before real port separator. Because brackets are used in
+    * hostname matching on lower layer, we can't use it. I decided to use
+    * angle brackets '<' '>' instead. */
+   if (buf[0] == '<' && NULL != (p = strchr(buf + 1, '>')))
+   {
+      *p++ = '\0';
+      buf++;
+
+      if (*p == '\0')
+      {
+         /* Only IPv6 address without port number */
+         p = NULL;
+      }
+      else if (*p != ':')
+      {
+         /* Garbage after address delimiter */
+         return JB_ERR_PARSE;
+      }
+   }
+   else
+   {
+      p = strchr(buf, ':');
+   }
+
    if (NULL != p)
    {
       *p++ = '\0';
@@ -1449,4 +1505,6 @@
   Local Variables:
   tab-width: 3
   end:
+
+  vim:softtabstop=3 shiftwidth=3
 */
